home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-12 | 10.3 KB | 261 lines | [TEXT/CCL2] |
- ;; -*- mode:lisp; syntax:common-lisp; package:cl-user -*-
- ;; file : rule-ants.lisp
- ;; author : Adam Alpern (ala@neural.hampshire.edu)
- ;; created : 07/16/94
- ;; version : 0.2
- ;; copyright: This particular implementation is ©1994 Adam Alpern.
- ;; synopsis: Something I whipped up in an evening after reading about
- ;; a neat little ant in "Mathematical Recreations".
- ;; This is just something I coded up very quickly because I
- ;; thought it would be easy and fun. Enjoy!
- ;; If you have any comments, suggestions, improvements, or
- ;; whatever, please send the to me at ala@neural.hampshire.edu.
- ;;
- ;; Langton's Ant:
- ;; Langton's ant is a very simple-minded creature in a simple world.
- ;; The antworld consists of a square grid, with each square in the grid
- ;; being either white or black (impl. note: squares which have not yet
- ;; been visted by the ant are colored gray. They are treated as if they
- ;; were white). The ant starts at an arbitrary point in the grid, with an
- ;; arbitrary heading, say east. The ant moves one square in it's direction.
- ;; If the square it lands on is black, the ant colors it white and turns 90
- ;; degrees to the left. If the square is white, the ant colors it black and
- ;; turns 90 degrees to the right. The neat part is, after around 10,000
- ;; steps, the ant will start building a highway!
- ;;
- ;; Rule Ants
- ;; The case of Langton's Ant may be generalized to ants which may have
- ;; an arbitrary rule-string composed of 1s and 0s. When an ant with a
- ;; rule-string with a length of n leaves a cell with color k, it increments
- ;; the color of the cell to k + 1, wrapping k = (n - 1) + 1 to 0, and then
- ;; turns left or right based on the value of the kth symbol in the rule-string.
- ;; If the kth symbol is 1, the ant turns 90 degrees to the right. If it is
- ;; 0, the nt turns 90 degrees to the left. Then it moves on to the next cell
- ;; and repeats.
- ;;
- ;; References
- ;; [1] Stewart, Ian "The Ultimate in Anty-Particles" in the Mathematical
- ;; Recreations column, Scientifc American, July 1994.
- ;;
- ;; Notes
- ;; Does no error-checking. If the ant happens to wander off the
- ;; edge of the grid, it will cause an array index out of bounds error.
- ;;
- ;; The classes ant, antworld, and ant-window should never be directly
- ;; created with make-instance. Instead, use make-antworld to create
- ;; the whole package.
- ;;
- ;; Usage
- ;; [function] make-antworld => (w ant-window)
- ;; make-antworld returns an instance of the class ant-window.
- ;; keyword args:
- ;; :x integer The horizontal size of the world.
- ;; :y integer The vertical size of the world.
- ;; :cellsize integer The size in pixels of a side of a cell.
- ;; :ant-x integer The horizontal location of the ant's
- ;; initial position.
- ;; :ant-y integer The vertical location of the ant's
- ;; initial position.
- ;; :heading { $east | $west | $north | $south }
- ;; :rule a string containing only 1s and 0s, i.e. "1100"
- ;; :colors a 1-dimensional array of MCL encoded colors,
- ;; equal in size or greater than the length of
- ;; the rule-string
- ;;
- ;; [method] run ((w ant-window) &optional (length :infinite)) => nil
- ;; Runs the ant in the world contained within the ant-window by calling
- ;; move-ant succesively on the ant. Returns nil.
- ;; Takes one optional argument:
- ;; length { :infinite | an integer } Runs until aborted
- ;; if length is :inifinte (the default), or else
- ;; executes the specified number of moves. If
- ;; is supplied, it must be either :infinite or
- ;; an integer.
- ;;
- ;; Revision History
- ;; 07/16/94 - file created, based on langtons-ant.lisp
- ;;
- ;; Example
- ;; This example gives the behaviour of Langton's ant using the
- ;; default values.
- ;; (setf foo (make-antworld))
- ;; (run foo)
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (in-package :cl-user)
- (require :quickdraw)
-
- (defconstant $east (list 'x 1))
- (defconstant $west (list 'x -1))
- (defconstant $north (list 'y -1))
- (defconstant $south (list 'y 1))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defclass ant ()
- ((x :initarg :x :initform 75 :accessor x)
- (y :initarg :y :initform 75 :accessor y)
- (heading :initarg :heading :initform $east :accessor heading)
- (world :initarg :world :initform nil :accessor world)
- (rule :initarg :rule :initform "10" :accessor rule)
- (colors :initarg :colors :initform #(0 16777215)
- :accessor colors)))
-
- (defclass antworld (view)
- ((x :initarg :x :initform 150 :accessor x)
- (y :initarg :y :initform 150 :accessor y)
- (grid :initarg :grid :initform nil :accessor grid)
- (ant :initarg :ant :initform nil :accessor ant)
- (cellsize :initarg :cellsize :initform 2 :accessor cellsize)
- (name :initarg :name :initform (gensym "antworld-") :accessor name)))
-
- (defclass ant-window (window)
- ((world :initarg :world :initform nil :accessor world)
- (name :initarg :name :initform (gensym "ant-window-") :accessor name))
- (:default-initargs :color-p t :grow-icon-p nil))
-
- (defmethod ant ((w ant-window))
- (ant (world w)))
-
- (defmethod run ((w ant-window) &optional (length :infinite))
- (cond ((equal length :infinite)
- (loop (move-ant (ant w))))
- ((numberp length)
- (dotimes (i length)
- (move-ant (ant w))))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defmethod initialize-instance :after ((w antworld) &rest args)
- (declare (ignore args))
- (set-view-size w (* (cellsize w) (x w)) (* (cellsize w) (y w)))
- (setf (grid w) (make-array (list (x w) (y w))
- :initial-element 0)))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defmethod view-draw-contents ((w antworld))
- (with-focused-view w
- (with-fore-color *Light-Gray-Color*
- (fast-paint-rect 0 0 (point-h (view-size w)) (point-v (view-size w))
- *Light-Gray-Color*))))
-
- (defun fast-paint-rect (left &optional top right bot color)
- "A version of PAINT-RECT that does not focus the view -- should only
- be called within a WITH-FOCUSED-VIEW."
- (with-fore-color color
- (ccl::with-rectangle-arg (r left top right bot) (#_PaintRect r))))
-
- (defun draw-cell (w x y colors)
- (with-focused-view w
- (fast-paint-rect (* x (cellsize w)) (* y (cellsize w))
- (+ (* x (cellsize w)) (cellsize w))
- (+ (* y (cellsize w)) (cellsize w))
- (aref colors (aref (grid w) x y)))))
-
- (defun increment-cell (w x y rule)
- (setf (aref (grid w) x y)
- (mod (+ 1 (aref (grid w) x y))
- (length rule))))
-
- (defun increment-and-draw-cell (w x y colors rule)
- (increment-cell w x y rule)
- (draw-cell w x y colors))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun make-antworld (&key (x 150) (y 150) (cellsize 2)
- (ant-x 75) (ant-y 75)
- (rule "10")
- (heading $east)
- (colors #(0 16777215)))
- "This should always be called to create a new antworld. ants, antworlds,
- and ant-windows should never be directly instatiated with make-instance."
- (let (a w wind)
- (setq w (make-instance 'antworld :x x :y y :cellsize cellsize))
- (setq a (make-instance 'ant :world w :x ant-x :y ant-y
- :heading heading
- :colors colors
- :rule rule))
- (setf (ant w) a)
- (setf (world a) w)
- (setq wind (make-instance 'ant-window :world w))
- (set-view-size wind (* (cellsize w) (x w)) (* (cellsize w) (y w)))
- (set-window-title wind (princ-to-string (name w)))
- (add-subviews wind w)
- wind))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun move-ant (ant)
- (let ((world (world ant)) ; the ants' world
- (k (aref (grid (world ant)) (x ant) (y ant)))) ; the color/rule index
-
- (increment-and-draw-cell world (x ant) (y ant) ; increment the
- (colors ant) (rule ant)) ; vacated cell
-
- (case (elt (rule ant) k) ; change the ants' heading appropriately
- (#\0 ; 0 : turn left 90 degrees
- (cond
- ((equalp (heading ant) $east) (setf (heading ant) $north))
- ((equalp (heading ant) $west) (setf (heading ant) $south))
- ((equalp (heading ant) $north) (setf (heading ant) $west))
- ((equalp (heading ant) $south) (setf (heading ant) $east))))
- (#\1 ; 1 : turn right 90 degrees
- (cond
- ((equalp (heading ant) $east) (setf (heading ant) $south))
- ((equalp (heading ant) $west) (setf (heading ant) $north))
- ((equalp (heading ant) $north) (setf (heading ant) $east))
- ((equalp (heading ant) $south) (setf (heading ant) $west)))))
-
- (if (equalp (first (heading ant)) 'x) ; move the ant
- (setf (x ant) (+ (x ant) (second (heading ant)))) ; in the appropriate
- (setf (y ant) (+ (y ant) (second (heading ant))))); direction
-
- ))
-
- #|
-
- (defun make-gray-gradient ()
- (let ((step 255)
- (gradient (make-array 256)))
- (dotimes (i 256)
- (setf (aref gradient i)
- (make-color (* step i) (* step i) (* step i))))
- gradient))
-
- ;; this ant uses 256 shades of grey and a 256 symbol rule-string
- ;; made of a repeated 4-symbol rule, and generates bilaterally
- ;; symmetrical patterns.
-
- (setf florgle (make-antworld
- :rule (concatenate 'string
- "11001100110011001100110011001100"
- "11001100110011001100110011001100"
- "11001100110011001100110011001100"
- "11001100110011001100110011001100"
- "11001100110011001100110011001100"
- "11001100110011001100110011001100"
- "11001100110011001100110011001100"
- "11001100110011001100110011001100")
- :colors (make-gray-gradient)
- :cellsize 1
- :heading $east))
- (run florgle :infinite)
-
- ;; Ant 1100 creates infinitely many bilaterally
- ;; symmetrical patterns
- (setf baz (make-antworld
- :rule "1100"
- :colors `#(,*black-Color* ,*gray-Color*
- 9474192 ,*white-Color*)
- :cellsize 2
- :heading $east))
- (run baz 10000)
-
- ;; Langton's Ant
- (progn (setf bar (make-antworld))
- (run bar))
-
- |#